parser grammar UniASTParser;

options {
	tokenVocab = UniASTLexer;
}

rules: ruleSpec+ EOF;

ruleSpec: parserRuleSpec | lexerRuleSpec;

parserRuleSpec:
	ParserRuleName Colon ruleBlock extra_decls? Semi;

extra_decls:
	LeftBrace extra_decl (Comma extra_decl)* RightBrace;

extra_decl: extraNodeName Assign extraNodeRightValue;

extraNodeName: identifier;

extraNodeRightValue:
	extraNodeConstructure
	| identifier
	| StringLiteral;

extraNodeConstructure:
	extraNodeConstructureName Less extraNodeRightValue (
		Comma extraNodeRightValue
	)* Greater;

extraNodeConstructureName: identifier;

ruleBlock: ruleAltList;

ruleAltList: actionAlt (AlterOp actionAlt)*;

actionAlt: alternative;

alternative: element+;

element: atomOrGroup ebnfSuffix?;

atomOrGroup: atom | group;

atom_suffix: Less identifier Greater;

atom:
	terminalDef atom_suffix?	# suffixed_terminaldef
	| ruleref atom_suffix?		# suffixed_ruleref;

terminalDef: LexerRuleName | StringLiteral | LEXER_CHAR_SET;

ruleref: ParserRuleName;

setElement: LexerRuleName | StringLiteral | characterRange;

characterRange: StringLiteral Range StringLiteral;

blockSet: LeftParen setElement (AlterOp setElement)* RightParen;

ebnfSuffix: QuestionMark | Star | Plus;

group: LeftParen altList RightParen;

altList: alternative (AlterOp alternative)*;

// lexer rule
lexerRuleSpec: LexerRuleName Colon lexerRuleBlock Semi;

lexerRuleBlock: lexerAltList;

lexerAltList: lexerAlt (AlterOp lexerAlt)*;

lexerAlt: lexerElements lexerCommands?;

// E.g., channel(HIDDEN), skip, more, mode(INSIDE), push(INSIDE), pop
lexerCommands: Arror lexerCommand (Comma lexerCommand)*;

lexerCommand: lexerCommandName;

lexerCommandName: identifier;

lexerElements: lexerElement+;

lexerElement: lexerAtomOrGroup ebnfSuffix?;

lexerAtomOrGroup: lexerAtom | lexerGroup;

lexerAtom: characterRange | terminalDef;

lexerGroup: LeftParen lexerAltList RightParen;

identifier: LexerRuleName | ParserRuleName;